﻿using System;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using metroioc;
using metroioc.tests.Artefacts;

namespace MetroIoc.Tests
{
    [TestClass]
    public class ChildContainerTests
    {
        [TestMethod]
        public void ChildContainerCanRegisterWithoutExceptions()
        {
            var threw = false;

            try
            {
                var childContainer = new MetroContainer().CreateChildContainer();
            }
            catch
            {
                threw = true;
            }
            finally
            {
                Assert.IsFalse(threw, "ChildContainer should have registered without throwing an exception");
            }
        }

        [TestMethod]
        public void ChildContainerResolvesRegisteredTypes()
        {
            IContainer childContainer = new MetroContainer()
                .CreateChildContainer();

            childContainer.Register<IFoo, Foo>();

            var foo = childContainer.Resolve<IFoo>();

            Assert.IsNotNull(foo);
            Assert.IsInstanceOfType(foo, typeof (Foo));
        }

        [TestMethod]
        public void ChildContainerResolvesTypesRegisteredInParent()
        {
            IContainer container = new MetroContainer()
                .Register<IFoo, Foo>();

            var childContainer = container.CreateChildContainer();

            var foo = childContainer.Resolve<IFoo>();

            Assert.IsNotNull(foo);
            Assert.IsInstanceOfType(foo, typeof (Foo));
        }

        [TestMethod]
        public void ChildContainerThrowsIfNothingRegistered()
        {
            var childContainer = new MetroContainer()
                .CreateChildContainer();

            Assert.ThrowsException<ResolutionException>(
                () => childContainer.Resolve<IFoo>());
        }

        [TestMethod]
        public void ChildContainerResolvesChainedDependencyFromParentContainer()
        {
            var container = new MetroContainer()
                .Register<IFoo, Foo>();

            var childContainer = container.CreateChildContainer();

            var bar = childContainer.Resolve<Bar>();
        }

        [TestMethod]
        public void ChildContainerShouldCheckParentFirst()
        {
            var container = new MetroContainer()
                .Register<IFoo, Foo>()
                .Register<Bar>(null, new Singleton());

            var childContainer = container.CreateChildContainer();

            var parentBar = container.Resolve<Bar>();
            var childBar = childContainer.Resolve<Bar>();

            Assert.AreSame(childBar, parentBar);
        }

        [TestMethod]
        public void ChildContainerShouldResolveSingletonsFromParent()
        {
            var container = new MetroContainer()
                .Register<FooService>(registration: new Singleton());

            var childContainer = container
                .CreateChildContainer()
                .Register<IInterface, Implementation>()
                .Register<IOtherInterface, OtherImplementation>();

            var service = childContainer.Resolve<FooService>();
            var impl = childContainer.Resolve<IInterface>();

            Assert.AreEqual(service.Id, impl.Service.Id);
        }
    }


    internal interface IInterface
    {
        FooService Service { get; }
    }

    internal interface IOtherInterface
    {
    }

    internal class Implementation : IInterface
    {
        private readonly FooService _fooService;

        public Implementation(FooService fooService)
        {
            _fooService = fooService;
        }

        public FooService Service
        {
            get { return _fooService; }
        }
    }

    internal class OtherImplementation : IOtherInterface
    {

    }

    internal class FooService
    {
        private readonly IOtherInterface _otherInterface;

        public FooService(IOtherInterface otherInterface)
        {
            _otherInterface = otherInterface;
        }

        private string _id;

        public string Id
        {
            get { return _id ?? (_id = CreateId()); }
        }

        private static string CreateId()
        {
            var rand = new Random(DateTime.Now.Millisecond);
            return rand.Next(100000).ToString();
        }


        [TestMethod]
        public void ChildContainerShouldResolveSingletonsFromParent()
        {
            var container = new MetroContainer()
                .Register<FooService>(registration: new Singleton());

            var childContainer = container
                .CreateChildContainer()
                .Register<IInterface, Implementation>()
                .Register<IOtherInterface, OtherImplementation>();

            var service = childContainer.Resolve<FooService>();
            var impl = childContainer.Resolve<IInterface>();

            Assert.AreEqual(service.Id, impl.Service.Id);
        }
    }
}
